// SPDX-License-Identifier: GPL-2.0
/*  Himax Android Driver Sample Code for incell ic core functions
 *
 *  Copyright (C) 2021 Himax Corporation.
 *
 *  This software is licensed under the terms of the GNU General Public
 *  License version 2,  as published by the Free Software Foundation,  and
 *  may be copied,  distributed,  and modified under those terms.
 *
 *  This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "himax_ic_core.h"

static uint8_t *g_internal_buffer;
uint8_t flash_lock_type;
uint32_t dbg_reg_ary[4] = { addr_fw_dbg_msg_addr, addr_cs_central_state,
			    addr_chk_dd_status, addr_flag_reset_event };

unsigned long FW_VER_MAJ_FLASH_ADDR;
unsigned long FW_VER_MIN_FLASH_ADDR;
unsigned long CFG_VER_MAJ_FLASH_ADDR;
unsigned long CFG_VER_MIN_FLASH_ADDR;
unsigned long CID_VER_MAJ_FLASH_ADDR;
unsigned long CID_VER_MIN_FLASH_ADDR;
uint32_t CFG_TABLE_FLASH_ADDR;

void himax_mcu_burst_enable(uint8_t auto_add_4_byte)
{
	uint8_t tmp_data[DATA_LEN_4];
	int ret;

	/*I("%s,Entering\n", __func__);*/

	tmp_data[0] = (para_AHB_INC4 | auto_add_4_byte);

	ret = himax_bus_write(addr_AHB_INC4, tmp_data, 1,
			      HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) {
		E("%s: i2c access fail!\n", __func__);
		return;
	}
}

int himax_mcu_register_read(uint32_t read_addr, uint32_t read_length,
			    uint8_t *read_data)
{
	uint8_t tmp_data[DATA_LEN_4];
	int ret = 0;

	/*I("%s,Entering\n",__func__);*/
	
	if (read_length > FLASH_RW_MAX_LEN) {
		E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN);
		return LENGTH_FAIL;
	}

	if (read_length > DATA_LEN_4)
		himax_mcu_burst_enable(1);
	else
		himax_mcu_burst_enable(0);

	himax_parse_assign_cmd(read_addr, tmp_data, sizeof(tmp_data));

	ret = himax_bus_write(addr_AHB_address_byte_0, tmp_data, DATA_LEN_4,
			      HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) {
		E("%s: i2c access fail!\n", __func__);
		return I2C_FAIL;
	}

	tmp_data[0] = para_AHB_access_direction_read;

	ret = himax_bus_write(addr_AHB_access_direction, tmp_data, 1,
			      HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) {
		E("%s: i2c access fail!\n", __func__);
		return I2C_FAIL;
	}

	ret = himax_bus_read(addr_AHB_rdata_byte_0, read_data, read_length,
			     HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) {
		E("%s: i2c access fail!\n", __func__);
		return I2C_FAIL;
	}

	if (read_length > DATA_LEN_4)
		himax_mcu_burst_enable(0);

	return NO_ERR;
}

static int himax_mcu_flash_write_burst_lenth(uint8_t *reg_byte,
					     uint8_t *write_data,
					     uint32_t length)
{
	uint8_t *data_byte;
	int ret = 0;

	if (!g_internal_buffer) {
		E("%s: internal buffer not initialized!\n", __func__);
		return MEM_ALLOC_FAIL;
	}
	data_byte = g_internal_buffer;

	/* assign addr 4bytes */
	memcpy(data_byte, reg_byte, ADDR_LEN_4);
	/* assign data n bytes */
	memcpy(data_byte + ADDR_LEN_4, write_data, length);

	ret = himax_bus_write(addr_AHB_address_byte_0, data_byte,
			      length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) {
		E("%s: xfer fail!\n", __func__);
		return I2C_FAIL;
	}

	return NO_ERR;
}

int himax_mcu_register_write(uint32_t write_addr, uint32_t write_length,
			     uint8_t *write_data)
{
	int address;
	uint8_t tmp_addr[4];
	uint8_t *tmp_data;
	int total_read_times = 0;
	uint32_t max_bus_size = MAX_I2C_TRANS_SZ;
	uint32_t total_size_temp = 0;
	unsigned int i = 0;
	int ret = 0;

	/*I("%s,Entering\n", __func__);*/

	total_size_temp = write_length;

	himax_parse_assign_cmd(write_addr, tmp_addr, sizeof(tmp_addr));

	if (total_size_temp % max_bus_size == 0)
		total_read_times = total_size_temp / max_bus_size;
	else
		total_read_times = total_size_temp / max_bus_size + 1;

	if (write_length > DATA_LEN_4)
		himax_mcu_burst_enable(1);
	else
		himax_mcu_burst_enable(0);

	for (i = 0; i < (total_read_times); i++) {
		/* I("[log]write %d time start!\n", i);
		 * I("[log]addr[3]=0x%02X, addr[2]=0x%02X,
			addr[1]=0x%02X,	addr[0]=0x%02X!\n",
			tmp_addr[3], tmp_addr[2],
			tmp_addr[1], tmp_addr[0]);
		 * I("%s, write addr = 0x%02X%02X%02X%02X\n",
			__func__, tmp_addr[3], tmp_addr[2],
			tmp_addr[1], tmp_addr[0]);
		 */

		if (total_size_temp >= max_bus_size) {
			tmp_data = write_data + (i * max_bus_size);

			ret = himax_mcu_flash_write_burst_lenth(
				tmp_addr, tmp_data, max_bus_size);
			if (ret < 0) {
				I("%s: i2c access fail!\n", __func__);
				return I2C_FAIL;
			}
			total_size_temp = total_size_temp - max_bus_size;
		} else {
			tmp_data = write_data + (i * max_bus_size);
			/* I("last total_size_temp=%d\n",
			 *	total_size_temp % max_bus_size);
			 */
			ret = himax_mcu_flash_write_burst_lenth(
				tmp_addr, tmp_data, total_size_temp);
			if (ret < 0) {
				I("%s: i2c access fail!\n", __func__);
				return I2C_FAIL;
			}
		}

		/*I("[log]write %d time end!\n", i);*/
		address = ((i + 1) * max_bus_size);
		tmp_addr[0] = (write_addr & 0xFF) + (uint8_t)((address)&0x00FF);

		if (tmp_addr[0] < (write_addr & 0xFF))
			tmp_addr[1] = ((write_addr >> 8) & 0xFF) +
				      (uint8_t)((address >> 8) & 0x00FF) + 1;
		else
			tmp_addr[1] = ((write_addr >> 8) & 0xFF) +
				      (uint8_t)((address >> 8) & 0x00FF);

		udelay(100);
	}

	return NO_ERR;
}

static int himax_write_read_reg(uint32_t addr_32, uint8_t *data, uint8_t hb,
				uint8_t lb)
{
	int cnt = 0;
	uint8_t tmp_data[4] = { 0 };

	himax_mcu_register_read(addr_32, DATA_LEN_4, tmp_data);
	tmp_data[1] = data[1];
	tmp_data[0] = data[0];

	do {
		himax_mcu_register_write(addr_32, DATA_LEN_4, tmp_data);
		usleep_range(10000, 11000);
		himax_mcu_register_read(addr_32, DATA_LEN_4, tmp_data);
		/* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X,
		 *	[2]=0x%02X,[3]=0x%02X\n",
		 *	__func__, tmp_data[0],
		 *	tmp_data[1], tmp_data[2], tmp_data[3]);
		 */
	} while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100);

	if (cnt >= 100)
		return HX_RW_REG_FAIL;

	I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", addr_32,
	  tmp_data[1], tmp_data[0]);
	return NO_ERR;
}

void himax_mcu_interface_on(void)
{
	uint8_t tmp_data[DATA_LEN_4];
	uint8_t tmp_data2[DATA_LEN_4];
	int cnt = 0;
	int ret = 0;

	/* Read a dummy register to wake up I2C.*/
	ret = himax_bus_read(addr_AHB_rdata_byte_0, tmp_data, DATA_LEN_4,
			     HIMAX_I2C_RETRY_TIMES);
	if (ret < 0) { /* to knock I2C*/
		E("%s: i2c access fail!\n", __func__);
		return;
	}

	do {
		tmp_data[0] = para_AHB_continous;

		ret = himax_bus_write(addr_AHB_continous, tmp_data, 1,
				      HIMAX_I2C_RETRY_TIMES);
		if (ret < 0) {
			E("%s: i2c access fail!\n", __func__);
			return;
		}

		tmp_data[0] = para_AHB_INC4;

		ret = himax_bus_write(addr_AHB_INC4, tmp_data, 1,
				      HIMAX_I2C_RETRY_TIMES);
		if (ret < 0) {
			E("%s: i2c access fail!\n", __func__);
			return;
		}

		/*Check cmd*/
		himax_bus_read(addr_AHB_continous, tmp_data, 1,
			       HIMAX_I2C_RETRY_TIMES);
		himax_bus_read(addr_AHB_INC4, tmp_data2, 1,
			       HIMAX_I2C_RETRY_TIMES);

		if (tmp_data[0] == para_AHB_continous &&
		    tmp_data2[0] == para_AHB_INC4)
			break;

		usleep_range(1000, 1100);
	} while (++cnt < 10);

	if (cnt > 0)
		I("%s:Polling burst mode: %d times\n", __func__, cnt);
}

#define WIP_PRT_LOG "%s: retry:%d, bf[0]=%d, bf[1]=%d,bf[2]=%d, bf[3]=%d\n"
bool himax_mcu_wait_wip(int Timing)
{
	uint8_t tmp_data[DATA_LEN_4];
	int retry_cnt = 0;

	himax_parse_assign_cmd(data_spi200_trans_fmt, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_trans_fmt, DATA_LEN_4, tmp_data);
	tmp_data[0] = 0x01;

	do {
		himax_parse_assign_cmd(data_spi200_trans_ctrl_1, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_cmd_1, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		himax_mcu_register_read(addr_spi200_data, DATA_LEN_4, tmp_data);

		if ((tmp_data[0] & 0x01) == 0x00)
			return true;

		retry_cnt++;

		if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 ||
		    tmp_data[2] != 0x00 || tmp_data[3] != 0x00)
			I(WIP_PRT_LOG, __func__, retry_cnt, tmp_data[0],
			  tmp_data[1], tmp_data[2], tmp_data[3]);

		if (retry_cnt > 100) {
			E("%s: Wait wip error!\n", __func__);
			return false;
		}

		msleep(Timing);
	} while ((tmp_data[0] & 0x01) == 0x01);

	return true;
}

/*power saving level*/
static void himax_mcu_init_psl(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };

	himax_parse_assign_cmd(data_clear, data, sizeof(data));
	himax_mcu_register_write(addr_psl, DATA_LEN_4, data);
	I("%s: power saving level reset OK!\n", __func__);
}

void himax_mcu_power_on_init(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };

	I("%s:entering\n", __func__);
	himax_parse_assign_cmd(data_clear, data, sizeof(data));
	/*RawOut select initial*/
	himax_mcu_register_write(addr_raw_out_sel, DATA_LEN_4, data);
	/*DSRAM func initial*/
	himax_mcu_assign_sorting_mode(data);
	/* N frame initial :　reset N frame back to default value 1 for normal mode
	 */
	himax_mcu_register_write(addr_set_frame_addr, DATA_LEN_4, data);
	/*FW reload done initial*/
	himax_mcu_register_write(addr_fw_define_2nd_flash_reload, DATA_LEN_4,
				 data);
	I("%s: fp_sense_on = 0x1 \n", __func__);	
	g_core_fp.fp_sense_on(0x01);
}

bool himax_mcu_dd_reg_write(uint8_t addr, uint8_t pa_num, int len,
			    uint8_t *data, uint8_t bank)
{
	/*Calculate total write length*/
	uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4;
	uint8_t w_data[500];
	uint32_t tmp_addr_32 = 0;
	uint8_t tmp_addr[4] = { 0 };
	uint8_t tmp_data[4] = { 0 };
	bool *chk_data;
	uint32_t chk_idx = 0;
	int i = 0;

	chk_data = kcalloc(data_len, sizeof(bool), GFP_KERNEL);
	if (chk_data == NULL) {
		E("%s Allocate chk buf failed\n", __func__);
		return false;
	}

	memset(w_data, 0, 500);

	/*put input data*/
	chk_idx = pa_num % 4;
	for (i = 0; i < len; i++) {
		w_data[chk_idx] = data[i];
		chk_data[chk_idx++] = true;
	}

	/*get original data*/
	chk_idx = (pa_num / 4) * 4;
	for (i = 0; i < data_len; i++) {
		if (!chk_data[i]) {
			himax_mcu_dd_reg_read(addr, (uint8_t)(chk_idx + i), 1,
					      tmp_data, bank);

			w_data[i] = tmp_data[0];
			chk_data[i] = true;
		}
		D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]);
	}

	tmp_addr[3] = 0x30;
	tmp_addr[2] = addr >> 4;
	tmp_addr[1] = (addr << 4) | (bank * 4);
	tmp_addr[0] = chk_idx;
	kfree(chk_data);

	tmp_addr_32 = tmp_addr[3] << 24 | tmp_addr[2] << 16 | tmp_addr[1] << 8 |
		      tmp_addr[0];
	D("%s Addr = 0x%08X.\n", __func__, tmp_addr_32);

	return (himax_mcu_register_write(tmp_addr_32, data_len, w_data) ==
		NO_ERR);
}

bool himax_mcu_dd_reg_read(uint8_t addr, uint8_t pa_num, int len, uint8_t *data,
			   uint8_t bank)
{
	uint32_t tmp_addr_32 = 0;
	uint8_t tmp_addr[4] = { 0 };
	uint8_t tmp_data[4] = { 0 };
	int i = 0;

	for (i = 0; i < len; i++) {
		tmp_addr[3] = 0x30;
		tmp_addr[2] = addr >> 4;
		tmp_addr[1] = (addr << 4) | (bank * 4);
		tmp_addr[0] = pa_num + i;

		tmp_addr_32 = tmp_addr[3] << 24 | tmp_addr[2] << 16 |
			      tmp_addr[1] << 8 | tmp_addr[0];
		if (himax_mcu_register_read(tmp_addr_32, DATA_LEN_4, tmp_data))
			goto READ_FAIL;

		data[i] = tmp_data[0];

		D("%s Addr = 0x%08X .data = %2X\n", __func__,
		  tmp_addr_32, data[i]);
	}
	return true;

READ_FAIL:
	E("%s Read DD reg Failed.\n", __func__);
	return false;
}

/* CORE_FW */
/* FW side start*/
static void diag_mcu_parse_raw_data(struct himax_report_data *hx_touch_data,
				    int mul_num, int self_num, uint8_t diag_cmd,
				    int32_t *mutual_data, int32_t *self_data)
{
	int RawDataLen_word;
	int index = 0;
	int temp1, temp2, i;

	if ((hx_touch_data->hx_rawdata_buf[0] == 0x3a) &&
	    (hx_touch_data->hx_rawdata_buf[1] == 0xa3) &&
	    hx_touch_data->hx_rawdata_buf[2] > 0 &&
	    hx_touch_data->hx_rawdata_buf[3] == diag_cmd) {
		RawDataLen_word = hx_touch_data->rawdata_size / 2;
		index = (hx_touch_data->hx_rawdata_buf[2] - 1) *
			RawDataLen_word;

		/* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n",index,
		 *	buf[56], buf[57], buf[58], buf[59], mul_num, self_num);
		 * I("RawDataLen=%d , RawDataLen_word=%d ,
		 *	hx_touch_info_size=%d\n",
		 *	RawDataLen, RawDataLen_word, hx_touch_info_size);
		 */
		for (i = 0; i < RawDataLen_word; i++) {
			temp1 = index + i;

			if (temp1 < mul_num) { /*mutual*/
				mutual_data[index + i] =
					((int8_t)hx_touch_data
						 ->hx_rawdata_buf[i * 2 + 4 +
								  1]) *
						256 +
					hx_touch_data->hx_rawdata_buf[i * 2 + 4];
			} else { /*self*/
				temp1 = i + index;
				temp2 = self_num + mul_num;

				if (temp1 >= temp2)
					break;

				self_data[i + index - mul_num] =
					(((int8_t)hx_touch_data
						  ->hx_rawdata_buf[i * 2 + 4 +
								   1])
					 << 8) +
					hx_touch_data->hx_rawdata_buf[i * 2 + 4];
			}
		}
	}
}

/*-------------------------------------------------------------------------
 *
 *	Create: Unknown
 *
 *	Description:  Do software reset by setting addr 0x90000018 register with
 *value 0x55. Parameters: void
 *
 *	Returns: void
 *
 *	Side effects: None
 *
 */
void himax_mcu_system_reset(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };

#if defined(HIMAX_I2C_PLATFORM)
	int ret = 0;
#endif

	I("%s: Entering!\n", __func__);

	himax_parse_assign_cmd(data_system_reset, data, sizeof(data));
	himax_mcu_register_write(addr_system_reset, DATA_LEN_4, data);

	msleep(100);

#if defined(HIMAX_I2C_PLATFORM)
	ret = himax_bus_read(addr_AHB_rdata_byte_0, data, DATA_LEN_4,
			     HIMAX_I2C_RETRY_TIMES);

	if (ret < 0)
		E("%s: i2c access fail!\n", __func__);

#endif
}

void himax_mcu_command_reset(void)
{
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	int ret = 0;

	I("%s: Entering!\n", __func__);

	/* reset code*/
	tmp_data[0] = 0x00;
	tmp_data[1] = 0x00;

	ret = himax_bus_write(addr_sense_on_off_0, tmp_data, 1,
			      HIMAX_I2C_RETRY_TIMES);
	if (ret < 0)
		E("%s: i2c access fail!\n", __func__);

	ret = himax_bus_write(addr_sense_on_off_1, tmp_data, 1,
			      HIMAX_I2C_RETRY_TIMES);
	if (ret < 0)
		E("%s: i2c access fail!\n", __func__);

	msleep(20);

#if defined(HIMAX_I2C_PLATFORM)
	ret = himax_bus_read(addr_AHB_rdata_byte_0, tmp_data, DATA_LEN_4,
			     HIMAX_I2C_RETRY_TIMES);
	if (ret < 0)
		E("%s: i2c access fail!\n", __func__);

#endif
}

int himax_mcu_calculate_CRC32_by_AP(unsigned char *FW_content, int CRC_from_FW,
				    int len)
{
	int i, j, length = 0;
	int fw_data;
	int fw_data_2;
	int CRC = 0xFFFFFFFF;
	int PolyNomial = 0x82F63B78;

	length = len / 4;

	for (i = 0; i < length; i++) {
		fw_data = FW_content[i * 4];

		for (j = 1; j < 4; j++) {
			fw_data_2 = FW_content[i * 4 + j];
			fw_data += (fw_data_2) << (8 * j);
		}
		CRC = fw_data ^ CRC;
		for (j = 0; j < 32; j++) {
			if ((CRC % 2) != 0)
				CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial;
			else
				CRC = (((CRC >> 1) & 0x7FFFFFFF));
		}
	}

	return CRC;
}

uint32_t himax_mcu_check_CRC(uint8_t *start_addr, int reload_length)
{
	uint32_t result = 0;
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	int cnt = 0, ret = 0;
	int length = reload_length / DATA_LEN_4;

	tmp_data[0] = 0xA5;
	himax_mcu_register_write(addr_retry_wrapper_clr_pw, 4, tmp_data);

	ret = himax_mcu_register_write(addr_reload_addr_from, DATA_LEN_4,
				       start_addr);
	if (ret < NO_ERR) {
		E("%s: i2c access fail!\n", __func__);
		return HW_CRC_FAIL;
	}

	tmp_data[3] = 0x00;
	tmp_data[2] = 0x99;
	tmp_data[1] = (length >> 8);
	tmp_data[0] = length;

	ret = himax_mcu_register_write(addr_reload_addr_cmd_beat, DATA_LEN_4,
				       tmp_data);
	if (ret < NO_ERR) {
		E("%s: i2c access fail!\n", __func__);
		return HW_CRC_FAIL;
	}
	cnt = 0;

	do {
		ret = himax_mcu_register_read(addr_reload_status, DATA_LEN_4,
					      tmp_data);
		if (ret < NO_ERR) {
			E("%s: i2c access fail!\n", __func__);
			return HW_CRC_FAIL;
		}

		if ((tmp_data[0] & 0x01) != 0x01) {
			ret = himax_mcu_register_read(addr_reload_crc32_result,
						      DATA_LEN_4, tmp_data);

			if (ret < NO_ERR) {
				E("%s: i2c access fail!\n", __func__);
				return HW_CRC_FAIL;
			}
			I("%s:data[3]=%X,data[2]=%X,data[1]=%X,data[0]=%X\n",
			  __func__, tmp_data[3], tmp_data[2], tmp_data[1],
			  tmp_data[0]);
			result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) +
				  (tmp_data[1] << 8) + tmp_data[0]);
			goto END;
		} else {
			usleep_range(1000, 1100);
			if (cnt >= 100)
				himax_mcu_read_FW_status();
		}

	} while (cnt++ < 100);
END:
	return result;
}

void himax_mcu_set_SMWP_enable(uint8_t SMWP_enable, bool suspended)
{
	uint8_t tmp_data[DATA_LEN_4];
	uint8_t back_data[DATA_LEN_4];
	uint8_t retry_cnt = 0;

	do {
		if (SMWP_enable) {
			himax_parse_assign_cmd(fw_func_handshaking_pwd,
					       tmp_data, 4);
			himax_mcu_register_write(fw_addr_smwp_enable,
						 DATA_LEN_4, tmp_data);
			himax_parse_assign_cmd(fw_func_handshaking_pwd,
					       back_data, 4);
		} else {
			himax_parse_assign_cmd(
				fw_data_safe_mode_release_pw_reset, tmp_data,
				4);
			himax_mcu_register_write(fw_addr_smwp_enable,
						 DATA_LEN_4, tmp_data);
			himax_parse_assign_cmd(
				fw_data_safe_mode_release_pw_reset, back_data,
				4);
		}

		himax_mcu_register_read(fw_addr_smwp_enable, DATA_LEN_4,
					tmp_data);
		/*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n",
		 *	__func__, tmp_data[0],SMWP_enable,retry_cnt);
		 */
		retry_cnt++;
	} while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] ||
		  tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) &&
		 retry_cnt < HIMAX_REG_RETRY_TIMES);
}

#define PRT_DATA "%s:[3]=0x%2X, [2]=0x%2X, [1]=0x%2X, [0]=0x%2X\n"
void himax_mcu_diag_register_set(uint8_t diag_command, uint8_t storage_type,
				 bool convert_diag)
{
	uint8_t tmp_data[DATA_LEN_4];
	uint8_t back_data[DATA_LEN_4];
	uint8_t cnt = 50;

	if (diag_command > 0 && storage_type % 8 > 0 && !convert_diag)
		tmp_data[0] = diag_command + 0x08;
	else
		tmp_data[0] = diag_command;
	I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]);
	himax_mcu_interface_on();
	tmp_data[3] = 0x00;
	tmp_data[2] = 0x00;
	tmp_data[1] = 0x00;
	do {
		himax_mcu_register_write(addr_raw_out_sel, DATA_LEN_4,
					 tmp_data);
		himax_mcu_register_read(addr_raw_out_sel, DATA_LEN_4,
					back_data);
		I(PRT_DATA, __func__, back_data[3], back_data[2], back_data[1],
		  back_data[0]);
		cnt--;
	} while (tmp_data[0] != back_data[0] && cnt > 0);
}

#define PRT_TMP_DATA "%s:[0]=0x%2X,[1]=0x%2X,	[2]=0x%2X,[3]=0x%2X\n"
void himax_mcu_reload_disable(int disable)
{
	uint8_t data[DATA_LEN_4] = { 0 };

	if (disable) { /*reload disable*/
		himax_parse_assign_cmd(data_fw_define_flash_reload_dis, data,
				       sizeof(data));
		himax_mcu_register_write(addr_fw_define_flash_reload,
					 DATA_LEN_4, data);
	} else { /*reload enable*/
		himax_parse_assign_cmd(data_fw_define_flash_reload_en, data,
				       sizeof(data));
		himax_mcu_register_write(addr_fw_define_flash_reload,
					 DATA_LEN_4, data);
	}

	I("%s: setting OK!\n", __func__);
}

void himax_mcu_rawdata_normalize_disable(int disable)
{
	uint8_t data[DATA_LEN_4] = { 0 };

	I("%s:entering\n", __func__);
	himax_mcu_register_read(addr_fw_define_rawdata_normalize, DATA_LEN_4,
				data);

	if (disable) /*normalize disable*/
		data[3] &= (~0x80);
	else /*normalize enable*/
		data[3] |= 0x80;

	himax_mcu_register_write(addr_fw_define_rawdata_normalize, DATA_LEN_4,
				 data);
}

int himax_mcu_read_ic_trigger_type(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	int trigger_type = false;

	himax_mcu_register_read(addr_fw_define_int_is_edge, DATA_LEN_4, data);

	if ((data[1] & 0x01) == 1)
		trigger_type = true;

	return trigger_type;
}

int himax_mcu_read_i2c_status(void)
{
	return i2c_error_count;
}

void himax_mcu_read_FW_ver(void)
{
	uint8_t data[12] = { 0 };
	uint8_t data_2[DATA_LEN_4] = { 0 };
	uint8_t retry = 0;
	uint8_t reload_status = 0;

	while (reload_status == 0) {
		himax_mcu_register_read(addr_fw_define_flash_reload, DATA_LEN_4,
					data);
		himax_mcu_register_read(addr_fw_define_2nd_flash_reload,
					DATA_LEN_4, data_2);

		if ((data[2] == 0x9A && data[3] == 0xA9) ||
		    (data_2[1] == 0x72 && data_2[0] == 0xC0)) {
			I("%s: FW finish reload done %d times\n", __func__,
			  retry);
			reload_status = 1;
			break;
		} else if (retry == 200) {
			E("%s: FW fail reload done !!!!!\n", __func__);
			himax_mcu_read_FW_status();
			ic_data->vendor_panel_ver = 0;
			ic_data->vendor_arch_ver = 0;
			ic_data->vendor_config_ver = 0;
			ic_data->vendor_touch_cfg_ver = 0;
			ic_data->vendor_display_cfg_ver = 0;
			ic_data->vendor_cid_maj_ver = 0;
			ic_data->vendor_cid_min_ver = 0;
			goto END;
		} else {
			retry++;
			usleep_range(10000, 11000);
		}
	}
	/*I("%s:data_2[0]=0x%2.2X,data_2[1]=0x%2.2X\n", __func__, data_2[0],
	 * data_2[1]);
	 */
	/*
	 * Read FW version
	 */
	himax_mcu_register_read(addr_fw_architecture_version, DATA_LEN_4, data);
	ic_data->vendor_panel_ver = data[0];
	ic_data->vendor_arch_ver = data[1] << 8 | data[2];

	himax_mcu_register_read(addr_fw_config_version, DATA_LEN_4, data);
	ic_data->vendor_config_ver = data[2] << 8 | data[3];
	ic_data->vendor_touch_cfg_ver = data[2];
	ic_data->vendor_display_cfg_ver = data[3];

	himax_mcu_register_read(addr_fw_CID, DATA_LEN_4, data);
	ic_data->vendor_cid_maj_ver = data[2];
	ic_data->vendor_cid_min_ver = data[3];

	himax_mcu_register_read(addr_fw_customer, 12, data);
	memcpy(ic_data->vendor_cus_info, data, 12);

	himax_mcu_register_read(addr_fw_project_name, 12, data);
	memcpy(ic_data->vendor_proj_info, data, 12);

	himax_mcu_register_read(addr_fw_config_date, 12, data);
	memcpy(ic_data->vendor_config_date, data, 12);

	I("Architecture Version : %X\n", ic_data->vendor_arch_ver);
	I("CID : %04X\n",
	  (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver));
	I("FW Display Config. Version : %X\n", ic_data->vendor_display_cfg_ver);
	I("FW Touch Config. Version : %X\n", ic_data->vendor_touch_cfg_ver);
	I("Panel Version : %X\n", ic_data->vendor_panel_ver);
	I("Config. Date = %s\n", ic_data->vendor_config_date);
	I("Project Name = %s\n", ic_data->vendor_proj_info);
	I("Customer = %s\n", ic_data->vendor_cus_info);
	I("Himax Touch Driver Version = %s\n", HIMAX_DRIVER_VER);

END:
	return;
}

void himax_print_define_function(void)
{
	struct himax_ts_data *ts = private_ts;

	I("HX_BOOT_UPGRADE : %d\n", HX_BOOT_UPGRADE);
	I("HX_EXCP_RECOVERY : %d\n", HX_EXCP_RECOVERY);
	I("HX_PROTOCOL_A : %d\n", HX_PROTOCOL_A);
	I("HX_PROTOCOL_B_3PA : %d\n", HX_PROTOCOL_B_3PA);
	I("HX_RST_PIN_FUNC : %d\n", HX_RST_PIN_FUNC);
	I("HX_TP_INSPECT_MODE : %d\n", HX_TP_INSPECT_MODE);
	I("HX_FIX_TOUCH_INFO : %d\n", HX_FIX_TOUCH_INFO);
	I("FCA_PROTOCOL_EN : %d\n", FCA_PROTOCOL_EN);
	I("HX_WPBP_ENABLE : %d\n", HX_WPBP_ENABLE);
	I("HX_SMART_WAKEUP : %d\n", HX_SMART_WAKEUP);
	I("HX_GESTURE_TRACK : %d\n", HX_GESTURE_TRACK);
	I("GTS_range : %d\n", ts->GTS_range);
}

bool himax_mcu_read_event_stack(uint8_t *buf, uint8_t length)
{
	int len = length;
	int i2c_speed = 0;
	struct timespec64 timeStart, timeEnd, timeDelta;

	if (private_ts->debug_log_level & BIT(2))
		ktime_get_real_ts64(&timeStart);

	himax_bus_read(addr_read_event_stack, buf, length,
		       HIMAX_I2C_RETRY_TIMES);

	if (private_ts->debug_log_level & BIT(2)) {
		ktime_get_real_ts64(&timeEnd);
		timeDelta.tv_nsec = timeEnd.tv_nsec - timeStart.tv_nsec;

		i2c_speed =
			(len * 9 * 1000000 / (int)timeDelta.tv_nsec) * 13 / 10;
		private_ts->bus_speed = (int)i2c_speed;
	}

	return 1;
}

void himax_mcu_stop_DSRAM_output(void)
{
	uint8_t tmp_data[DATA_LEN_4] = { 0 };

	if (himax_write_read_reg(addr_rawdata, tmp_data, 0x00, 0x00) < 0)
		I("%s Data NOT ready => bypass\n", __func__);

	I("%s: End of setting!\n", __func__);
}

bool himax_mcu_calculateChecksum(uint32_t size)
{
	uint8_t CRC_result = 0;
	uint8_t tmp_addr[DATA_LEN_4] = { 0 };
	uint8_t tmp_data[DATA_LEN_4] = { 0 };
	int i = 0;

	I("%s:Now size= %dk\n", __func__, (size / 1024));
	himax_parse_assign_cmd(addr_program_reload_from, tmp_addr,
			       sizeof(tmp_addr));

	/* disable WDT */
	tmp_data[0] = 0x53;
	tmp_data[1] = 0xAC;
	himax_mcu_register_write(addr_WDT_disable, DATA_LEN_4, tmp_data);
	for (i = 0; i < 10; i++) {
		msleep(20);
		himax_mcu_register_read(addr_WDT_disable, DATA_LEN_4, tmp_data);
		I("%s:Disable WDT Read value %02X, %02X, %02X, %02X\n",
		  __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]);
		if ((tmp_data[1] == 0xAC) && (tmp_data[0] == 0x53))
			break;
	}

	CRC_result = himax_mcu_check_CRC(tmp_addr, size);
	msleep(50);

	if (CRC_result != 0)
		I("%s: CRC Fail=%d\n", __func__, CRC_result);

	/* enable WDT */
	tmp_data[0] = 0x00;
	tmp_data[1] = 0x00;
	himax_mcu_register_write(addr_WDT_disable, DATA_LEN_4, tmp_data);
	for (i = 0; i < 10; i++) {
		msleep(20);
		himax_mcu_register_read(addr_WDT_disable, DATA_LEN_4, tmp_data);
		I("%s:Enable WDT Read value %02X, %02X, %02X, %02X\n", __func__,
		  tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]);
		if ((tmp_data[1] == 0x00) && (tmp_data[0] == 0x00))
			break;
	}

	return (CRC_result == 0) ? true : false;
}

void himax_mcu_read_FW_status(void)
{
	uint8_t len = 0;
	uint8_t i = 0;
	uint8_t data[DATA_LEN_4] = { 0 };

	len = (uint8_t)(sizeof(dbg_reg_ary) / sizeof(uint32_t));

	for (i = 0; i < len; i++) {
		himax_mcu_register_read(dbg_reg_ary[i], DATA_LEN_4, data);

		I("reg[0-3] : 0x%08X = 0x%02X, 0x%02X, 0x%02X, 0x%02X\n",
		  dbg_reg_ary[i], data[0], data[1], data[2], data[3]);
	}
}

void himax_mcu_irq_switch(int switch_on)
{
	if (switch_on) {
		if (private_ts->use_irq)
			himax_int_enable(switch_on);
		else
			hrtimer_start(&private_ts->timer, ktime_set(1, 0),
				      HRTIMER_MODE_REL);
	} else {
		if (private_ts->use_irq)
			himax_int_enable(switch_on);
		else {
			hrtimer_cancel(&private_ts->timer);
			cancel_work_sync(&private_ts->work);
		}
	}
}

int himax_mcu_assign_sorting_mode(uint8_t *tmp_data)
{
	I("%s:data[1]=0x%2X,data[0]=0x%2X\n", __func__, tmp_data[1],
	  tmp_data[0]);

	himax_mcu_register_write(addr_sorting_mode_en, DATA_LEN_4, tmp_data);

	return NO_ERR;
}

int himax_mcu_check_sorting_mode(uint8_t *tmp_data)
{
	himax_mcu_register_read(addr_sorting_mode_en, DATA_LEN_4, tmp_data);
	I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0],
	  tmp_data[1]);

	return NO_ERR;
}

int himax_mcu_check_N_frame(uint8_t *tmp_data)
{
	himax_mcu_register_read(addr_set_frame_addr, DATA_LEN_4, tmp_data);
	I("%s: tmp_data[0]=%x\n", __func__, tmp_data[0]);

	return NO_ERR;
}

/* FW side end*/
/* CORE_FW */

/* CORE_FLASH */
/* FLASH side start*/
void himax_mcu_chip_erase(void)
{
	uint8_t tmp_data[DATA_LEN_4] = { 0 };

	himax_mcu_interface_on();

	himax_parse_assign_cmd(data_spi200_trans_fmt, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_trans_fmt, DATA_LEN_4, tmp_data);

	himax_parse_assign_cmd(data_spi200_trans_ctrl_2, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4, tmp_data);

	himax_parse_assign_cmd(data_spi200_cmd_2, tmp_data, sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

	himax_parse_assign_cmd(data_spi200_cmd_3, tmp_data, sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

	msleep(800);

	if (!himax_mcu_wait_wip(100))
		E("%s: Chip_Erase Fail\n", __func__);
}

static bool himax_mcu_block_erase(int start_addr, int length)
{
	uint32_t page_prog_start = 0;
	uint32_t block_size = 0x10000;
	uint8_t tmp_data[DATA_LEN_4] = { 0 };


	himax_parse_assign_cmd(data_spi200_trans_fmt, tmp_data,
			       sizeof(tmp_data));
	himax_mcu_register_write(addr_spi200_trans_fmt, DATA_LEN_4, tmp_data);

	for (page_prog_start = start_addr;
	     page_prog_start < start_addr + length;
	     page_prog_start = page_prog_start + block_size) {
		himax_parse_assign_cmd(data_spi200_trans_ctrl_2, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_cmd_2, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		tmp_data[3] = (page_prog_start >> 24) & 0xFF;
		tmp_data[2] = (page_prog_start >> 16) & 0xFF;
		tmp_data[1] = (page_prog_start >> 8) & 0xFF;
		tmp_data[0] = page_prog_start & 0xFF;
		himax_mcu_register_write(addr_spi200_addr, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_trans_ctrl_3, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_trans_ctrl, DATA_LEN_4,
					 tmp_data);

		himax_parse_assign_cmd(data_spi200_cmd_4, tmp_data,
				       sizeof(tmp_data));
		himax_mcu_register_write(addr_spi200_cmd, DATA_LEN_4, tmp_data);

		msleep(100);

		if (!himax_mcu_wait_wip(100)) {
			E("%s:Erase Fail\n", __func__);
			return false;
		}
	}

	I("%s:END\n", __func__);
	return true;
}

bool himax_mcu_sector_erase(int start_addr, int length)
{
	uint32_t tmp_addr_32 = 0;
	uint8_t data[DATA_LEN_4] = { 0 };
	uint32_t page_prog_start = 0;
	uint32_t sector_size = 0x1000;

	/*=====================================
	 *SPI Transfer Format : 0x8000_0010 ==> 0x0002_0780
	 *=====================================
	 */
	data[3] = 0x00;
	data[2] = 0x02;
	data[1] = 0x07;
	data[0] = 0x80;

	tmp_addr_32 = 0x80000010;
	himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);

	for (page_prog_start = start_addr;
	     page_prog_start < start_addr + length;
	     page_prog_start = page_prog_start + sector_size) {
		/*=====================================
		 *Write Enable : 1. 0x8000_0020 ==> 0x4700_0000 //control
		 *			 2. 0x8000_0024 ==> 0x0000_0006 //WREN
		 *=====================================
		 */
		data[3] = 0x47;
		data[2] = 0x00;
		data[1] = 0x00;
		data[0] = 0x00;
		tmp_addr_32 = 0x80000020;
		himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);
		data[3] = 0x00;
		data[2] = 0x00;
		data[1] = 0x00;
		data[0] = 0x06;
		tmp_addr_32 = 0x80000024;
		himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);

		/*=====================================
		 *Sector Erase
		 *Command : 0x8000_0028 ==> 0x0000_0000 //SPI addr
		 *				0x8000_0020 ==> 0x6700_0000 //control
		 *				0x8000_0024 ==> 0x0000_0020 //SE
		 *=====================================
		 */

		data[3] = (uint8_t)(page_prog_start >> 24);
		data[2] = (uint8_t)(page_prog_start >> 16);
		data[1] = (uint8_t)(page_prog_start >> 8);
		data[0] = (uint8_t)page_prog_start;

		tmp_addr_32 = 0x80000028;
		himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);
		data[3] = 0x67;
		data[2] = 0x00;
		data[1] = 0x00;
		data[0] = 0x00;
		tmp_addr_32 = 0x80000020;
		himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);
		data[3] = 0x00;
		data[2] = 0x00;
		data[1] = 0x00;
		data[0] = 0x20;
		tmp_addr_32 = 0x80000024;
		himax_mcu_register_write(tmp_addr_32, DATA_LEN_4, data);

		if (!himax_mcu_wait_wip(100))
			return false;
	}

	I("%s:END\n", __func__);
	return true;
}

int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(unsigned char *fw, int len,
						   bool change_iref)
{
	int burnFW_success = 0;
	uint8_t tmp_addr[4];
	struct timespec64  timeStart, timeEnd, timeDelta;

	ktime_get_real_ts64(&timeStart);

	if (len != FW_SIZE_128k) {
		E("%s: The file size is not 128K bytes\n", __func__);
		return false;
	}

	g_core_fp.fp_sense_off();
	himax_mcu_init_psl();

	himax_disable_flash_protected_mode();

	himax_mcu_block_erase(0x00, FW_SIZE_128k);
	g_core_fp.fp_flash_programming(fw, 0, FW_SIZE_128k);

	himax_parse_assign_cmd(addr_program_reload_from, tmp_addr,
			       sizeof(tmp_addr));
	if (himax_mcu_check_CRC(tmp_addr, FW_SIZE_128k) == 0)
		burnFW_success = 1;

	ktime_get_real_ts64(&timeEnd);
	timeDelta = time_diff(timeStart, timeEnd);
	I("<<Timer>>%s => %ld.%ld s\n", __func__, timeDelta.tv_sec,
	  timeDelta.tv_nsec);

	return burnFW_success;
}

#if (HX_BOOT_UPGRADE == 0x01)
bool himax_mcu_flash_lastdata_check_with_bin(uint32_t size)
{
	uint32_t start_addr = 0xFFFFFFFF;
	uint8_t flash_buffer[DATA_LEN_4];
	uint8_t i = 0;

	start_addr = size - DATA_LEN_4;
	himax_mcu_register_read(start_addr, DATA_LEN_4, &flash_buffer[0]);

	for (i = 1; i <= DATA_LEN_4; i++) {
		if (flash_buffer[DATA_LEN_4 - i] != hxfw->data[size - i]) {
			E("%s: Flash content is different from BIN file, Need Update\n",
			  __func__);
			I("FLASH[%08X] ~ FLASH[%08X] = %02X%02X%02X%02X\n",
			  size - 4, size - 1, flash_buffer[DATA_LEN_4 - 4],
			  flash_buffer[DATA_LEN_4 - 3],
			  flash_buffer[DATA_LEN_4 - 2],
			  flash_buffer[DATA_LEN_4 - 1]);
			I("BIN[%08X] ~ BIN[%08X] = %02X%02X%02X%02X\n",
			  size - 4, size - 1, hxfw->data[size - 4],
			  hxfw->data[size - 3], hxfw->data[size - 2],
			  hxfw->data[size - 1]);
			return 1;
		}
	}

	return 0;
}
#endif
static bool hx_bin_desc_data_get(uint32_t addr, uint8_t *flash_buf)
{
	uint8_t data_sz = 0x10;
	uint32_t i = 0, j = 0;
	uint16_t chk_end = 0;
	uint16_t chk_sum = 0;
	uint32_t map_code = 0;
	unsigned long flash_addr = 0;

	for (i = 0; i < FW_PAGE_SZ; i = i + data_sz) {
		for (j = i; j < (i + data_sz); j++) {
			chk_end |= flash_buf[j];
			chk_sum += flash_buf[j];
		}
		if (!chk_end) { /*1. Check all zero*/
			I("%s: End in %X\n", __func__, i + addr);
			return false;
		} else if (chk_sum % 0x100) /*2. Check sum*/
			I("%s: chk sum failed in %X\n", __func__, i + addr);
		else { /*3. get data*/
			map_code = flash_buf[i] + (flash_buf[i + 1] << 8) +
				   (flash_buf[i + 2] << 16) +
				   (flash_buf[i + 3] << 24);
			flash_addr = flash_buf[i + 4] +
				     (flash_buf[i + 5] << 8) +
				     (flash_buf[i + 6] << 16) +
				     (flash_buf[i + 7] << 24);
			switch (map_code) {
			case FW_CID:
				CID_VER_MAJ_FLASH_ADDR = flash_addr;
				CID_VER_MIN_FLASH_ADDR = flash_addr + 1;
				I("%s: CID in %lX\n", __func__,
				  CID_VER_MAJ_FLASH_ADDR);
				break;
			case FW_VER:
				FW_VER_MAJ_FLASH_ADDR = flash_addr;
				FW_VER_MIN_FLASH_ADDR = flash_addr + 1;
				I("%s: FW_VER in %lX\n", __func__,
				  FW_VER_MAJ_FLASH_ADDR);
				break;
			case CFG_VER:
				CFG_VER_MAJ_FLASH_ADDR = flash_addr;
				CFG_VER_MIN_FLASH_ADDR = flash_addr + 1;
				I("%s: CFG_VER in = %lX\n", __func__,
				  CFG_VER_MAJ_FLASH_ADDR);
				break;
			case TP_CONFIG_TABLE:
				CFG_TABLE_FLASH_ADDR = flash_addr;
				I("%s: CONFIG_TABLE in %X\n", __func__,
				  CFG_TABLE_FLASH_ADDR);
				break;
			}
		}
		chk_end = 0;
		chk_sum = 0;
	}

	return true;
}

bool hx_mcu_bin_desc_get(unsigned char *fw, uint32_t max_sz)
{
	uint32_t addr_t = 0;
	unsigned char *fw_buf = NULL;
	bool keep_on_flag = false;
	bool g_bin_desc_flag = false;

	do {
		fw_buf = &fw[addr_t];

		/*Check bin is with description table or not*/
		if (!g_bin_desc_flag) {
			if (fw_buf[0x00] == 0x00 && fw_buf[0x01] == 0x00 &&
			    fw_buf[0x02] == 0x00 && fw_buf[0x03] == 0x00 &&
			    fw_buf[0x04] == 0x00 && fw_buf[0x05] == 0x00 &&
			    fw_buf[0x06] == 0x00 && fw_buf[0x07] == 0x00 &&
			    fw_buf[0x0E] == 0x87)
				g_bin_desc_flag = true;
		}
		if (!g_bin_desc_flag) {
			I("%s: fw_buf[0x00] = %2X, fw_buf[0x0E] = %2X\n",
			  __func__, fw_buf[0x00], fw_buf[0x0E]);
			I("%s: No description table\n", __func__);
			break;
		}

		/*Get related data*/
		keep_on_flag = hx_bin_desc_data_get(addr_t, fw_buf);

		addr_t = addr_t + FW_PAGE_SZ;
	} while (max_sz > addr_t && keep_on_flag);

	return g_bin_desc_flag;
}

/* FLASH side end*/
/* CORE_FLASH */

/* CORE_SRAM */
bool himax_mcu_get_DSRAM_data(uint8_t *info_data, bool DSRAM_Flag)
{
	unsigned int i = 0;
	unsigned char tmp_addr[ADDR_LEN_4];
	unsigned char tmp_data[DATA_LEN_4];
	uint8_t max_i2c_size = MAX_I2C_TRANS_SZ;
	uint8_t x_num = ic_data->HX_RX_NUM;
	uint8_t y_num = ic_data->HX_TX_NUM;
	/*int m_key_num = 0;*/
	int total_size = (x_num * y_num + x_num + y_num) * 2 + 4;
	int total_data_size = (x_num * y_num + x_num + y_num) * 2;
	int total_size_temp;
	/*int mutual_data_size = x_num * y_num * 2;*/
	int total_read_times = 0;
	int address = 0;
	uint8_t *temp_info_data = NULL; /*max mkey size = 8*/
	uint16_t check_sum_cal = 0;
	int fw_run_flag = -1;

	temp_info_data = kcalloc((total_size + 8), sizeof(uint8_t), GFP_KERNEL);
	if (temp_info_data == NULL) {
		E("%s, Failed to allocate memory\n", __func__);
		return false;
	}
	/* 1. Read number of MKey R100070E8H to determin data size */
	/* I("%s,m_key_num=%d\n",__func__ ,m_key_num); */
	/* total_size += m_key_num * 2; */
	/* 2. Start DSRAM Rawdata and Wait Data Ready */
	tmp_data[3] = 0x00;
	tmp_data[2] = 0x00;
	tmp_data[1] = 0x5A;
	tmp_data[0] = 0xA5;
	fw_run_flag = himax_write_read_reg(addr_rawdata, tmp_data, 0xA5, 0x5A);

	if (fw_run_flag < 0) {
		I("%s Data NOT ready => bypass\n", __func__);
		himax_mcu_read_FW_status();
		goto FAIL;
	}

	/* 3. Read RawData */
	total_size_temp = total_size;

	if (total_size % max_i2c_size == 0)
		total_read_times = total_size / max_i2c_size;
	else
		total_read_times = total_size / max_i2c_size + 1;

	for (i = 0; i < total_read_times; i++) {
		address = addr_rawdata + i * max_i2c_size;
		/*I("%s address = %08X\n", __func__, address);*/

		if (total_size_temp >= max_i2c_size) {
			himax_mcu_register_read(
				address, max_i2c_size,
				&temp_info_data[i * max_i2c_size]);
			total_size_temp = total_size_temp - max_i2c_size;
		} else {
			/*I("last total_size_temp=%d\n",total_size_temp);*/
			himax_mcu_register_read(
				address, total_size_temp % max_i2c_size,
				&temp_info_data[i * max_i2c_size]);
		}
	}

	/* 4. FW stop outputing */
	tmp_data[3] = temp_info_data[3];
	tmp_data[2] = temp_info_data[2];
	tmp_data[1] = 0x00;
	tmp_data[0] = 0x00;
	himax_write_read_reg(addr_rawdata, tmp_data, 0x00, 0x00);

	/* 5. Data Checksum Check */
	for (i = 2; i < total_size; i += 2) /* 2:PASSWORD NOT included */
		check_sum_cal +=
			(temp_info_data[i + 1] * 256 + temp_info_data[i]);

	if (check_sum_cal % 0x10000 != 0) {
		I("%s check_sum_cal fail=%2X\n", __func__, check_sum_cal);
		goto FAIL;
	} else {
		memcpy(info_data, &temp_info_data[4],
		       total_data_size * sizeof(uint8_t));
		/*I("%s checksum PASS\n", __func__);*/
	}
	kfree(temp_info_data);
	return true;
FAIL:
	kfree(temp_info_data);
	return false;
}
/* CORE_SRAM */

#if (HX_BOOT_UPGRADE == 0x01)
/*-------------------------------------------------------------------------
 *
 *	Create: Unknown
 *
 *	Description:  Read FW_VER and CFG_VER value from FW file
 *
 *	Parameters: void
 *
 *	Returns: int (0 success/ 1 fail)
 *
 *	Side effects: None
 */
int himax_mcu_fw_ver_bin(void)
{
	I("%s: use default incell address.\n", __func__);
	if (hxfw != NULL) {
		I("Catch fw version in bin file!\n");
		g_i_FW_VER = (hxfw->data[FW_VER_MAJ_FLASH_ADDR] << 8) |
			     hxfw->data[FW_VER_MIN_FLASH_ADDR];
		g_i_CFG_VER = (hxfw->data[CFG_VER_MAJ_FLASH_ADDR] << 8) |
			      hxfw->data[CFG_VER_MIN_FLASH_ADDR];
		g_i_CID_MAJ = hxfw->data[CID_VER_MAJ_FLASH_ADDR];
		g_i_CID_MIN = hxfw->data[CID_VER_MIN_FLASH_ADDR];
	} else {
		I("FW data is null!\n");
		return 1;
	}
	return NO_ERR;
}
#endif

#if (HX_RST_PIN_FUNC == 0x01)
void himax_mcu_tp_lcm_pin_reset(void)
{
	I("%s: Now reset the Touch chip and LCM.\n", __func__);
	himax_gpio_set(private_ts->rst_gpio, 0);
	himax_gpio_set(private_ts->lcm_gpio, 0);
	msleep(60);
	himax_gpio_set(private_ts->lcm_gpio, 1);
	msleep(110);
	himax_gpio_set(private_ts->rst_gpio, 1);
	msleep(20); // because i2c will fail 1 time if no delay
}

static void himax_mcu_pin_reset(void)
{
	I("%s: Now reset the Touch chip.\n", __func__);
	himax_gpio_set(private_ts->rst_gpio, 0);
	msleep(20);
	himax_gpio_set(private_ts->rst_gpio, 1);
	msleep(50);
}

void himax_mcu_ic_reset(uint8_t loadconfig, uint8_t int_off)
{
	struct himax_ts_data *ts = private_ts;

	I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig,
	  int_off);

	if (ts->rst_gpio >= 0) {
		if (int_off)
			himax_mcu_irq_switch(0);

		himax_mcu_pin_reset();

		if (loadconfig)
			himax_mcu_reload_config();

		if (int_off)
			himax_mcu_irq_switch(1);
	}
}
#elif (HX_RST_PIN_FUNC == 0x02)
	/* Need Customer set TP reset pin and follow sequence time
	 * Need at least two function control
	 * 1.himax_mcu_tp_lcm_pin_reset control TP and LCM reset pin for AP recovery
	 * 2.himax_mcu_pin_reset simple tp hardware reset pin
	 */
#endif

/*-------------------------------------------------------------------------
 *
 *	Create: Unknown
 *
 *	Description: Read related touch information from mcu or assign fixed values
 *				to ic_data value.
 *	Parameters: void
 *
 *	Returns: void
 *
 *	Side effects: None
 *
 */
void himax_mcu_touch_information(void)
{
#if (HX_FIX_TOUCH_INFO == 0x00)
	uint8_t data[DATA_LEN_8] = { 0 };

	himax_mcu_register_read(addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_8,
				data);
	ic_data->HX_RX_NUM = data[2];
	ic_data->HX_TX_NUM = data[3];
	ic_data->HX_MAX_PT = data[4];

	himax_mcu_register_read(addr_fw_define_xy_res, DATA_LEN_8, data);
	ic_data->HX_X_REVERSE = (data[2] & 0x01);
	ic_data->HX_Y_REVERSE = ((data[2] & 0x02) >> 1);

	ic_data->HX_Y_RES = (data[6] << 8) | data[7];
	ic_data->HX_X_RES = (data[4] << 8) | data[5];

	himax_mcu_register_read(addr_fw_define_int_is_edge, DATA_LEN_4, data);
	ic_data->HX_INT_IS_EDGE = (data[1] & 0x01);

	himax_mcu_register_read(addr_HX_ID_EN, DATA_LEN_4, data);
	ic_data->HX_IS_ID_EN = ((data[1] & 0x02) >> 1);
	ic_data->HX_ID_PALM_EN = ((data[1] & 0x08) >> 7);

#else
	ic_data->HX_RX_NUM = FIX_HX_RX_NUM;
	ic_data->HX_TX_NUM = FIX_HX_TX_NUM;
	ic_data->HX_MAX_PT = FIX_HX_MAX_PT;
	ic_data->HX_INT_IS_EDGE = FIX_HX_INT_IS_EDGE;
	ic_data->HX_IS_ID_EN = FIX_HX_IS_ID_EN;
	ic_data->HX_ID_PALM_EN = FIX_HX_ID_PALM_EN;
	ic_data->HX_Y_RES = private_ts->pdata->screenHeight;
	ic_data->HX_X_RES = private_ts->pdata->screenWidth;
#endif

	private_ts->pdata->abs_x_min = 0;
	private_ts->pdata->abs_x_max = (ic_data->HX_X_RES - 1);
	private_ts->pdata->abs_y_min = 0;
	private_ts->pdata->abs_y_max = (ic_data->HX_Y_RES - 1);

	I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d\n", __func__, ic_data->HX_RX_NUM,
	  ic_data->HX_TX_NUM);
	I("%s:HX_X_REVERSE=%d,HX_Y_REVERSE =%d\n", __func__,
	  ic_data->HX_X_REVERSE, ic_data->HX_Y_REVERSE);
	I("%s:HX_Y_RES=%d,HX_X_RES =%d,HX_INT_IS_EDGE =%d,\n", __func__,
	  ic_data->HX_Y_RES, ic_data->HX_X_RES, ic_data->HX_INT_IS_EDGE);
	I("%s:HX_IS_ID_EN=%d \n", __func__, ic_data->HX_IS_ID_EN);
	I("%s:HX_ID_PALM_EN=%d \n", __func__, ic_data->HX_ID_PALM_EN);
}

void himax_mcu_reload_config(void)
{
	if (himax_report_data_init())
		E("%s: allocate data fail\n", __func__);

	g_core_fp.fp_sense_on(0x00);
}

int himax_mcu_get_touch_data_size(void)
{
	return HIMAX_TOUCH_DATA_SIZE;
}

int himax_mcu_cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max)
{
	int RawDataLen;

	if (raw_cnt_rmd != 0x00)
		RawDataLen = MAX_I2C_TRANS_SZ -
			     ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1;
	else
		RawDataLen = MAX_I2C_TRANS_SZ -
			     ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1;

	return RawDataLen;
}

bool himax_mcu_diag_check_sum(struct himax_report_data *hx_touch_data)
{
	uint16_t check_sum_cal = 0;
	int i;

	/* Check 128th byte CRC */
	for (i = 0, check_sum_cal = 0; i < (hx_touch_data->touch_all_size -
					    hx_touch_data->touch_info_size);
	     i += 2) {
		check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] *
					  FLASH_RW_MAX_LEN +
				  hx_touch_data->hx_rawdata_buf[i]);
	}

	if (check_sum_cal % HX64K != 0) {
		I("%s fail=%2X\n", __func__, check_sum_cal);
		return 0;
	}

	return 1;
}

void himax_mcu_diag_parse_raw_data(struct himax_report_data *hx_touch_data,
				   int mul_num, int self_num, uint8_t diag_cmd,
				   int32_t *mutual_data, int32_t *self_data)
{
	diag_mcu_parse_raw_data(hx_touch_data, mul_num, self_num, diag_cmd,
				mutual_data, self_data);
}
#if (HX_EXCP_RECOVERY == 0x01)
int himax_mcu_ic_excp_recovery(uint32_t hx_excp_event, uint32_t hx_zero_event,
			       uint32_t length)
{
	int ret_val = NO_ERR;

	if (hx_excp_event == length) {
		g_zero_event_count = 0;
		ret_val = HX_EXCP_EVENT;
	} else if (hx_zero_event == length) {
		if (g_zero_event_count > 5) {
			g_zero_event_count = 0;
			I("EXCEPTION event checked - ALL Zero.\n");
			ret_val = HX_EXCP_EVENT;
		} else {
			g_zero_event_count++;
			I("ALL Zero event is %d times.\n", g_zero_event_count);
			ret_val = HX_ZERO_EVENT_COUNT;
		}
	}

	return ret_val;
}

void himax_mcu_excp_ic_reset(void)
{
	HX_EXCP_RESET_ACTIVATE = 0;
#if (HX_RST_PIN_FUNC == 0x01)
	himax_mcu_pin_reset();
#elif (HX_RST_PIN_FUNC == 0x02)
	/* Need Customer do TP reset pin */
#else
	himax_mcu_system_reset();
#endif
	I("%s:\n", __func__);
}
#endif

/* CORE_INIT */

int himax_mcu_in_cmd_struct_init(void)
{
	int err = 0;

	g_internal_buffer =kzalloc(sizeof(uint8_t) * HX_MAX_WRITE_SZ, GFP_KERNEL);

	if (g_internal_buffer == NULL) {
		err = -ENOMEM;
		goto err_g_core_cmd_op_g_internal_buffer_fail;
	}

	return NO_ERR;

err_g_core_cmd_op_g_internal_buffer_fail:
	E("%s: err_g_core_cmd_op_g_internal_buffer_fail \n", __func__);
	return err;
}

void himax_mcu_in_cmd_struct_free(void)
{
	kfree(g_internal_buffer);
	g_internal_buffer = NULL;

	I("%s: release completed\n", __func__);
}

/*-------------------------------------------------------------------------
 *
 *	Create: 2021/07
 *
 *	Description: Disable WP BP protection for updating FW correctly.
 *
 *	Parameters: void
 *
 *	Returns: int (0 success/ -1 fail)
 *
 *	Side effects: None
 *
 *	+--------------+-----------+----+------+-----------+----------------+
 *	|      ID      |    BP     | WP | type | Lock code | delay time(ms) |
 *	+--------------+-----------+----+------+-----------+----------------+
 *	| 0            | 2_3       |  7 |    1 | 0x8C      | 10             |
 *	| 1/6/11/14/15 | 2_3_4     |  7 |    2 | 0x9C      | 1/0/1/2/2      |
 *	| 4/5/10/12/13 | 2_3_4_5   |  7 |    3 | 0xBC      | 10/0/5/2/2     |
 *	| 2/3/7/8/9    | 2_3_4_5_6 |  7 |    4 | 0xFC      | 40/30/8/8/8    |
 *	+--------------+-----------+----+------+-----------+----------------+
 *
 */
int himax_disable_flash_protected_mode(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	uint8_t loop_count = 0;

#if defined(WP_GPIO4)
	/*WP addr config */
	himax_parse_assign_cmd(data_WP_gpio4_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_pin_base, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio4_cmd_10, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio4_cmd_04, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio4_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio4_cmd_1C, DATA_LEN_4, data);

#elif defined(WP_GPIO0)
	/*WP addr config */
	himax_parse_assign_cmd(data_WP_gpio0_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_pin_base, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio0_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio0_cmd_04, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio0_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio0_cmd_0C, DATA_LEN_4, data);
#endif

	/*BP addr config */
	himax_parse_assign_cmd(data_BP_lock_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_10, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_2, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_3, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_4, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_5, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_2C, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_6, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	/*Delay as write status register cycle time, expect 20ms ~ 40ms*/
	/*<20ms will cause trouble 0x8000002C wrong return value*/

	/*Check BP addr */;
	himax_parse_assign_cmd(data_BP_check_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_check_cmd_2, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	/*Read Addr*/
	himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);

	while (data[0] != 0x00 && loop_count != 50) {
		himax_parse_assign_cmd(data_BP_check_cmd_2, data, sizeof(data));
		himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

		himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);
		loop_count++;
		if (loop_count == 50) {
			W("%s: disable WP BP lock with wrong return data :%02X, loop value : %d.\n",
			  __func__, data[0], loop_count);
			return -1;
		}
		usleep_range(1000, 1100);
	}

	if (data[0] != 0x00) {
		W("%s: disable WP BP lock with wrong return data :%02X, loop value : %d.\n",
		  __func__, data[0], loop_count);
		return -1;
	}

	I("%s: Disable WP BP lock finish.  value = %02X, loop value %d.\n",
	  __func__, data[0], loop_count);
	return 0;
}

/*-------------------------------------------------------------------------
 *
 *	Create: 2021/07
 *
 *	Description: Enable WP BP protection for protect FW status.
 *
 *	Parameters: void
 *
 *	Returns: int (0 success/ -1 fail)
 *
 *	Side effects: If not lock, cannot do any update with FW status
 *
 *	+--------------+-----------+----+------+-----------+----------------+
 *	|      ID      |    BP     | WP | type | Lock code | delay time(ms) |
 *	+--------------+-----------+----+------+-----------+----------------+
 *	| 0            | 2_3       |  7 |    1 | 0x8C      | 10             |
 *	| 1/6/11/14/15 | 2_3_4     |  7 |    2 | 0x9C      | 1/0/1/2/2      |
 *	| 4/5/10/12/13 | 2_3_4_5   |  7 |    3 | 0xBC      | 10/0/5/2/2     |
 *	| 2/3/7/8/9    | 2_3_4_5_6 |  7 |    4 | 0xFC      | 40/30/8/8/8    |
 *	+--------------+-----------+----+------+-----------+----------------+
 *
 */
int himax_enable_flash_protected_mode(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	int ret = 0;
	uint8_t lock_code = 0;
	uint8_t loop_count = 0;

	ret = himax_mcu_flash_id_check();

	if (ret == -1) {
		I("%s: Cannot recognize flash id type\n", __func__);
		return -1;
	}

	switch (flash_lock_type) {
	case 1:
		lock_code = 0x8C;
		break;
	case 2:
		lock_code = 0x9C;
		break;
	case 3:
		lock_code = 0xBC;
		break;
	case 4:
		lock_code = 0xFC;
		break;
	default:
		I("%s: Unknown lock type with value : %x\n", __func__,
		  flash_lock_type);
		break;
	}

	/*BP addr config */
	himax_parse_assign_cmd(data_BP_lock_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_10, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_2, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_3, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_4, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_7, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_2C, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_lock_cmd_6, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	/*Delay as write status register cycle time, expect 20ms ~ 40ms*/
	/*<20ms will cause trouble 0x8000002C wrong return value*/

	/*Check BP */
	himax_parse_assign_cmd(data_BP_check_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_check_cmd_2, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	/*Read Addr*/
	himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);

	while (((data[0] & lock_code) != lock_code) && loop_count != 50) {
		himax_parse_assign_cmd(data_BP_check_cmd_2, data, sizeof(data));
		himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

		himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);
		loop_count++;
		if (loop_count == 50) {
			W("%s: enable WP BP lock with wrong data :%02X, lock code %02X, loop value %d.\n",
			  __func__, data[0], lock_code, loop_count);
			return -1;
		}
		usleep_range(1000, 1100);
	}

	if ((data[0] & lock_code) != lock_code) {
		W("%s: enable WP BP lock with wrong data :%02X, lock code %02X, loop value %d.\n",
		  __func__, data[0], lock_code, loop_count);
		return -1;
	}

	I("%s: Enable WP BP lock finish. value = %02X, loop value %d.\n",
	  __func__, data[0], loop_count);

#if defined(WP_GPIO4)
	/*WP addr gpio4 */
	himax_parse_assign_cmd(data_WP_gpio4_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_pin_base, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio4_cmd_10, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio4_cmd_04, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio4_cmd_00, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio4_cmd_1C, DATA_LEN_4, data);
#elif defined(WP_GPIO0)
	/*WP addr gpio4 */
	himax_parse_assign_cmd(data_WP_gpio0_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_pin_base, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio0_cmd_01, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio0_cmd_04, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_WP_gpio0_cmd_00, data, sizeof(data));
	himax_mcu_register_write(addr_WP_gpio0_cmd_0C, DATA_LEN_4, data);
#endif

	return 0;
}

/*-------------------------------------------------------------------------
 *
 *	Create: 2021/08
 *
 *	Description: Check WP BP protection status.
 *
 *	Parameters: void
 *
 *	Returns: int (lock type code byte data)
 *
 *	Side effects: None
 *
 *	+--------------+-----------+----+------+-----------+----------------+
 *	|      ID      |    BP     | WP | type | Lock code | delay time(ms) |
 *	+--------------+-----------+----+------+-----------+----------------+
 *	| 0            | 2_3       |  7 |    1 | 0x8C      | 10             |
 *	| 1/6/11/14/15 | 2_3_4     |  7 |    2 | 0x9C      | 1/0/1/2/2      |
 *	| 4/5/10/12/13 | 2_3_4_5   |  7 |    3 | 0xBC      | 10/0/5/2/2     |
 *	| 2/3/7/8/9    | 2_3_4_5_6 |  7 |    4 | 0xFC      | 40/30/8/8/8    |
 *	+--------------+-----------+----+------+-----------+----------------+
 *
 */
int himax_mcu_WP_BP_status(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	int ret = 0;
	uint8_t lock_code = 0;

	ret = himax_mcu_flash_id_check();

	if (ret == -1) {
		I("%s: Cannot recognize flash id type\n", __func__);
		return -1;
	}

	switch (flash_lock_type) {
	case 1:
		lock_code = 0x8C;
		break;
	case 2:
		lock_code = 0x9C;
		break;
	case 3:
		lock_code = 0xBC;
		break;
	case 4:
		lock_code = 0xFC;
		break;
	default:
		I("%s: Unknown lock type with value : %x\n", __func__,
		  flash_lock_type);
		break;
	}

	/*Check Addr 0x8000_002C value, if 0x9C BP is lock*/

	himax_parse_assign_cmd(data_BP_lock_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_10, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_check_cmd_1, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_check_cmd_2, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);


	if ((data[0] & lock_code) == lock_code) {
		I("%s: WP BP lock status is lock, Addr 0x8000_002C, read value is %02X, lock_code value is %02X\n",
	 	 __func__, data[0], lock_code);
		return 1;
	} else {
		I("%s: WP BP lock status is unlock, Addr 0x8000_002C, read value is %02X, lock_code value is %02X\n",
	 	 __func__, data[0], lock_code);
		return 0;
	}
}

/*-------------------------------------------------------------------------
 *
 *	Create: 2021/08
 *
 *	Description: Check Flash ID and sort ID to Lock type.
 *
 *	Parameters: void
 *
 *	Returns: int (0 success/ -1 fail)
 *
 *	Side effects: None
 *
 *	+--------------+-----------+----+------+-----------+----------------+
 *	|      ID      |    BP     | WP | type | Lock code | delay time(ms) |
 *	+--------------+-----------+----+------+-----------+----------------+
 *	| 0            | 2_3       |  7 |    1 | 0x8C      | 10             |
 *	| 1/6/11/14/15 | 2_3_4     |  7 |    2 | 0x9C      | 1/0/1/2/2      |
 *	| 4/5/10/12/13 | 2_3_4_5   |  7 |    3 | 0xBC      | 10/0/5/2/2     |
 *	| 2/3/7/8/9    | 2_3_4_5_6 |  7 |    4 | 0xFC      | 40/30/8/8/8    |
 *	+--------------+-----------+----+------+-----------+----------------+
 *
 */
int himax_mcu_flash_id_check(void)
{
	uint8_t data[DATA_LEN_4] = { 0 };
	uint8_t i;
	uint8_t Flash_list_tmp[][3] = Flash_list;
	size_t len = sizeof(Flash_list_tmp) / (sizeof(uint8_t) * 3);
	uint8_t flash_idx;

	flash_idx = 0xff;

	/*Check Addr 0x8000_002C value, if 0x9C BP is lock*/

	himax_parse_assign_cmd(data_BP_check_cmd_3, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_20, DATA_LEN_4, data);

	himax_parse_assign_cmd(data_BP_check_cmd_4, data, sizeof(data));
	himax_mcu_register_write(addr_BP_lock_cmd_24, DATA_LEN_4, data);

	himax_mcu_register_read(addr_BP_lock_cmd_2C, DATA_LEN_4, data);

	/*I("%s: FlashList len : %zd, Flash type ID data = %X,%X,%X\n", __func__,
	 * len, data[0], data[1], data[2]);
	 */

	for (i = 0; i < len; i++) {
		if ((Flash_list_tmp[i][0] == data[0]) &&
		    (Flash_list_tmp[i][1] == data[1]) &&
		    (Flash_list_tmp[i][2] == data[2])) {
			flash_idx = i;
			break;
		}
	}

	I("%s: Flash id mapping to index : %d. lock data, %x, %x, %x\n", __func__,
	 flash_idx, data[0], data[1], data[2]);


	if (flash_idx == 0) {
		flash_lock_type = 1;
	} else if ((flash_idx == 1) || (flash_idx == 6) || (flash_idx == 11) ||
		   (flash_idx == 14) || (flash_idx == 15)) {
		flash_lock_type = 2;
	} else if ((flash_idx == 4) || (flash_idx == 5) || (flash_idx == 10) ||
		   (flash_idx == 12) || (flash_idx == 13)) {
		flash_lock_type = 3;
	} else if ((flash_idx == 2) || (flash_idx == 3) || (flash_idx == 7) ||
		   (flash_idx == 8) || (flash_idx == 9)) {
		flash_lock_type = 4;
	} else {
		flash_lock_type = 0xff;
	}

	if (flash_lock_type == 0xff) {
		I("%s: Flash_lock_type Unknown cause\n", __func__);
		return -1;
	}

	/*I("%s: Flash id check and sort finished.\n", __func__);*/
	return 0;
}

/* init end*/
/* CORE_INIT */
